package comet.util;


import java.awt.Dimension;

import java.util.Collection;
import java.util.Iterator;
import javax.swing.JFrame;

import org.apache.commons.collections15.Transformer; 

import com.hp.hpl.jena.ontology.ComplementClass;
import com.hp.hpl.jena.ontology.ConversionException;
import com.hp.hpl.jena.ontology.DatatypeProperty;
import com.hp.hpl.jena.ontology.IntersectionClass;
import com.hp.hpl.jena.ontology.ObjectProperty;
import com.hp.hpl.jena.ontology.OntClass;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.ontology.OntProperty;
import com.hp.hpl.jena.ontology.OntResource;
import com.hp.hpl.jena.ontology.UnionClass;


import edu.uci.ics.jung.algorithms.layout.FRLayout;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.graph.Graph;
import edu.uci.ics.jung.graph.UndirectedSparseMultigraph;
import edu.uci.ics.jung.visualization.BasicVisualizationServer;
import edu.uci.ics.jung.visualization.renderers.Renderer.VertexLabel.Position;



/**
 * Class that move an ontology on OntModel format to a graph.
 * <p>Each ontology's concept is a node and each ontology's property is an edge.
 * <p>Structural properties (as intersection of concepts, generalization...) are represented by an edge.
 * @author hacene_m
 *
 */
public class OWL2JungGraph {
	
	/**
	 * This method load an ontology on OntModel and transform it to a graph.
	 * @param docURI URI of the ontology represented
	 * @param locationURL addresse of a copy of the initial ontology 
	 * @return ontology in graph format
	 */
	public static Graph<OntResource, OntProperty> OWL2JungGraph(String docURI, String locationURL) {
		OntModel onto = OntologyHelper.loadOntology(docURI,locationURL);
		return OWL2JungGraph(onto);
	}
	
	/**
	 * This method constructs vertices and edges from the ontology given in parameters.
	 * @param ontology ontology algorithm describes in a graph
	 * @return ontology in graph format
	 */
	public static Graph<OntResource, OntProperty> OWL2JungGraph(OntModel ontology) { 
		//Graph<V,E> V is the type of the vertices, E is the type of the edges
		Graph<OntResource,OntProperty> graph = new UndirectedSparseMultigraph<OntResource, OntProperty>();		
		
		exportOntologyConcepts2Graph(ontology,graph);
		exportOntologyRelations2Graph(ontology, graph);

		return graph;
	}
	
	/**
	 * This method constructs graph's nodes from the ontology given in parameters.
	 * @param ontology ontology in which algorithm exports concepts
	 * @param graph ontology in graph format
	 */
	public static void exportOntologyConcepts2Graph(OntModel ontology, Graph<OntResource, OntProperty> graph) {
		for (Iterator<OntClass> i = ontology.listClasses();  i.hasNext(); ) {
			OntClass oc = (OntClass) i.next();
			
		    graph.addVertex(oc);			
		}
		exportRangeOfDatatypeProperties2Graph(ontology,graph);
	}	
	
	/**
	 * This method exports concepts involved in Datatype properties (represented properties' range).
	 * @param ontology ontology in which algorithm draws Datatype concepts
	 * @param graph ontology in graph format
	 */
	public static void exportRangeOfDatatypeProperties2Graph(OntModel ontology, Graph<OntResource, OntProperty> graph) {
		Iterator<DatatypeProperty> i = ontology.listDatatypeProperties();
		while (i.hasNext()) {
			DatatypeProperty p = i.next();
			if (p.getLocalName() == null) continue;
			if (p.getRange() == null) continue;
			OntResource or = (OntResource) p.getRange();
			OntClass oc = or.asClass();
			if (oc.getLocalName() == null)
				OntologyHelper.grantClassNameForAnonymousClass(oc);
			else
				oc.setLabel(oc.getLocalName(), null);
			
			graph.addVertex(oc);
		}
	}
	
	/**
	 * This method constructs edges of the graph representing ontology given in parameters.
	 * @param ontology ontology in which relations are searched
	 * @param graph ontology in graph format
	 */
	public static void exportOntologyRelations2Graph(OntModel ontology, Graph<OntResource, OntProperty> graph) {
			exportObjectProperties2Graph(ontology,graph);
			exportDatatypeProperties2Graph(ontology,graph);
			exportLinks2Graph(ontology,graph);
	}
	
	/**
	 * This method exports object properties as edges.
	 * @param ontology ontology in which object properties are defined
	 * @param graph ontology in a graph format
	 */
	private static void exportObjectProperties2Graph(OntModel ontology, Graph<OntResource, OntProperty> graph) {
		Iterator<ObjectProperty> i = ontology.listObjectProperties();
		while (i.hasNext()) {
			ObjectProperty p = (ObjectProperty) i.next();
			if(p.getLocalName()==null) continue;
			if (p.getDomain() == null || p.getRange() == null) continue;
			graph.addEdge(p, p.getDomain(), p.getRange());
		}
	}
	
	/**
	 * This method represents datatype properties as edges.
	 * @param ontology ontology which contains datatype properties represented
	 * @param graph ontology in graph format
	 */
	private static void exportDatatypeProperties2Graph(OntModel ontology, Graph<OntResource, OntProperty> graph) {
		Iterator<DatatypeProperty> i = ontology.listDatatypeProperties();
		while (i.hasNext()) {
			DatatypeProperty p = (DatatypeProperty) i.next();
			if (p.getEquivalentProperty() != null) continue;
			if (p.getDomain() == null || p.getRange() == null) continue;
			graph.addEdge(p, p.getDomain(), p.getRange());
		}
	}

	/**
	 * This method constructs architectural relations existing in the ontology given in parameters.
	 * <p>Relations can be inheritance, equivalence, union, intersection...
	 * @param ontology ontology in which relations are defined
	 * @param graph representation of ontology given in parameters as a graph
	 */
	private static void exportLinks2Graph(OntModel ontology, Graph<OntResource, OntProperty> graph){		
		Collection<OntResource> vertices = graph.getVertices();
		Iterator<OntResource> verticesIt = vertices.iterator();
		while(verticesIt.hasNext()) {
			OntResource or = verticesIt.next();
			OntClass oc = or.asClass();
			 //Link inheritances Classes
			 Iterator<OntClass> subClasses = oc.listSubClasses();
			 while(subClasses.hasNext()) {
				 OntClass subClass = (OntClass)subClasses.next();
				 OntProperty inheritance = ontology.createOntProperty("IS-A" + subClass + "****" + oc);
				 graph.addEdge(inheritance, (OntResource)subClass, (OntResource)oc);				 
			 }
			 //Link equivalents Classes
			 Iterator<OntClass> equiClasses = oc.listEquivalentClasses();
			 while (equiClasses.hasNext()) {
				 OntClass equiClass = (OntClass) equiClasses.next();
				 OntProperty equiClassProp = ontology.createOntProperty("EQUIV" + oc+ "****" + equiClass);
				 graph.addEdge(equiClassProp, (OntResource)oc, (OntResource)equiClass);
			 }
			 //Link Disjoint Classes
			 Iterator<OntClass> disjClasses = oc.listDisjointWith();
			 boolean elementsInIterator = true;
			 while (elementsInIterator) {
				 try {
					 while (disjClasses.hasNext()) {
						 OntClass disjClass = (OntClass) disjClasses.next();
						 OntProperty disjClassProp = ontology.createOntProperty("DISJ-W" + oc + "****" + disjClass);
						 graph.addEdge(disjClassProp, (OntResource)oc, (OntResource)disjClass);
					 }
					 if (disjClasses.hasNext() == false) {
						 elementsInIterator = false;
					 }
				 }
				 catch (ConversionException ce) {
				 }
			 }

			 //Link Union Class
			 if (oc.isUnionClass()) {
				 UnionClass uc = oc.asUnionClass();
				 Iterator<?> uniClasses = uc.listOperands();
				 elementsInIterator = true;
				 while (elementsInIterator) {
					 try {
						 while (uniClasses.hasNext()) {
							 OntClass uniClass = (OntClass) uniClasses.next();
							 OntProperty uniClassProp = ontology.createOntProperty("UNION" + oc + "****" + uniClass);
							 graph.addEdge(uniClassProp, (OntResource)oc, (OntResource)uniClass);
						 }
						 if (uniClasses.hasNext() == false) {
							 elementsInIterator = false;
						 }
					 }
					 catch (ConversionException ce) {
					 }
				 }

			 }
			 //Link Intersection Class
			 if (oc.isIntersectionClass()) {
				 IntersectionClass ic = oc.asIntersectionClass();
				 Iterator<?> intClasses = ic.listOperands();
				 elementsInIterator = true;
				 while (elementsInIterator) {
					 try {
						 while (intClasses.hasNext()) {
							 OntClass intClass = (OntClass) intClasses.next();
							 OntProperty intClassProp = ontology.createOntProperty("INTERS" + oc + "****" + intClass);
							 graph.addEdge(intClassProp, (OntResource)oc, (OntResource)intClass);
						 }
						 if (intClasses.hasNext() == false) {
							 elementsInIterator = false;
						 }
					 }
					 catch (ConversionException ce) {
					 }
				 }

			 }
			 //Link Complement Class
			 if (oc.isComplementClass()) {
				 ComplementClass cc = oc.asComplementClass();
				 OntClass complClass = cc.getOperand();
				 OntProperty complClassProp = ontology.createOntProperty("COMPL" + oc + "****" + complClass);
				 graph.addEdge(complClassProp, (OntResource)oc, (OntResource)complClass);
			 } 
		}
		
	}
		
	/**
	 * This method print a graph and display it in a component.
	 * @param graph group of vertices and edges which will be displayed
	 */
	public static void displayGraph(Graph<OntResource, OntProperty> graph) {
		//Transformer for Vertex Label
		Transformer<OntResource, String> nameVertex = new Transformer<OntResource, String>() {
			public String transform(OntResource ont) {
				return ont.getLabel(null);
			}
		};
		
		//Transformer for Edge Label
		Transformer<OntProperty, String> nameEdge = new Transformer<OntProperty, String>() {
			public String transform(OntProperty ont) {
				String name = ont.toString();
				String label = "";
				if (name.contains("IS-A"))
					label = ont.toString().substring(0, 4);
				else if (name.contains("EQUIV"))
					label = ont.toString().substring(0, 5);
				else if (name.contains("DISJ-W"))
					label = ont.toString().substring(0, 6);
				else if (name.contains("UNION"))
					label = ont.toString().substring(0, 5);
				else if (name.contains("INTERS"))
					label = ont.toString().substring(0, 6);
				else if (name.contains("COMPL"))
					label = ont.toString().substring(0, 5);
				else
					label = ont.getLocalName();
				//return ont.toString()/*.substring(0, 3)*/;
				return label;
			}
		};
		
		Layout<OntResource, OntProperty> layout = new FRLayout<OntResource, OntProperty>(graph);
		layout.setSize(new Dimension(700, 700));
		
		BasicVisualizationServer<OntResource, OntProperty> vv = new BasicVisualizationServer<OntResource, OntProperty>(layout);
		vv.setPreferredSize(new Dimension(750, 750));	
		vv.getRenderContext().setVertexLabelTransformer(nameVertex);
		vv.getRenderer().getVertexLabelRenderer().setPosition(Position.CNTR);
		vv.getRenderContext().setEdgeLabelTransformer(nameEdge);
		
		JFrame frame = new JFrame("Ontology graph");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.getContentPane().add(vv);
		frame.pack();
		frame.setVisible(true);
	}
	
}
